package main

import (
	"fmt"
)

const fileFilter = "JPEG Image File(*.jpg *.jpeg);;All File(*)"

type Root = JpegImage

type JpegImage []JpegSection

type JpegSection struct {
	Sign         JpegSign
	Type         JpegType
	CompressData CompressData
	Sos          *SosBody
	Dqt          *DqtBody
	App0         *App0Body
	Dft          *DefaultBody
}

func (v *JpegSection) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Sign, &ValueInfo{name: "Sign", isBig: true})
	dec.Decode(&v.Type, &ValueInfo{name: "Type", isBig: true})
	switch v.Type {
	case 0xD8, 0xD9:
		break
	case 0xDA:
		v.Sos = &SosBody{}
		dec.Decode(&v.Sos, &ValueInfo{name: "Body", isBig: true})
		dec.Decode(&v.CompressData, &ValueInfo{name: "CompressData", isBig: true})
	case 0xDB:
		v.Dqt = &DqtBody{}
		dec.Decode(&v.Dqt, &ValueInfo{name: "Body", isBig: true})
	case 0xE0:
		v.App0 = &App0Body{}
		dec.Decode(&v.App0, &ValueInfo{name: "Body", isBig: true})
	default:
		v.Dft = &DefaultBody{}
		dec.Decode(&v.Dft, &ValueInfo{name: "Body", isBig: true})
	}
}

type JpegSign []byte

func (v *JpegSign) Unmarshal(dec *Decoder, info *ValueInfo) {
	pos := dec.Pos()
	bs := dec.ReadBytes(1)
	if len(bs) != 1 || bs[0] != 0xFF {
		panic("JPEG sign error")
	}
	*v = bs

	bs = dec.ReadBytes(1)
	for len(bs) == 1 && bs[0] == 0xFF {
		*v = append(*v, 0xFF)
		bs = dec.ReadBytes(1)
	}
	dec.Seek(pos + len(*v)*8)
}

type JpegType uint8

func (v JpegType) String() string {
	switch v {
	case 0xC0:
		return "SOF(Start of Frame)"
	case 0xC4:
		return "DHT(Huffman Table)"
	case 0xD8:
		return "SOI(Start of Image)"
	case 0xD9:
		return "EOI(End of Image)"
	case 0xDA:
		return "SOS(Start of Scan)"
	case 0xDB:
		return "DQT(Define Quantization Table)"
	case 0xDD:
		return "DRI(Define Restart Interval)"
	default:
		break
	}
	if 0xD0 <= v && v <= 0xD7 {
		d := uint8(v - 0xD0)
		return fmt.Sprintf("RST%X(Reset Marker d%d)", d, d)
	} else if 0xE0 <= v && v <= 0xEF {
		d := uint8(v - 0xE0)
		return fmt.Sprintf("APP%X(Application %d)", d, d)
	}
	return fmt.Sprintf("0x%02X", uint8(v))
}

type DefaultBody struct {
	Length uint16
	Data   []byte
}

func (v *DefaultBody) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Length, &ValueInfo{name: "Length", isBig: true})
	newDec := dec.SubDecoder(int(v.Length - 2))
	newDec.Decode(&v.Data, &ValueInfo{name: "Data", isBig: true})
	dec.Seek(newDec.Pos())
}

type SosBody struct {
	Length          uint16
	ColorComponents uint8
	Color           []ColorComponent
	SpectrumBegin   uint8
	SpectrumEnd     uint8
	Spectrum        uint8
}

func (v *SosBody) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Length, &ValueInfo{name: "Length", isBig: true})
	dec.Decode(&v.ColorComponents, &ValueInfo{name: "ColorComponents", isBig: true})
	v.Color = make([]ColorComponent, v.ColorComponents)
	dec.Decode(&v.Color, &ValueInfo{name: "Color", isBig: true})
	dec.Decode(&v.SpectrumBegin, &ValueInfo{name: "SpectrumBegin", isBig: true})
	dec.Decode(&v.SpectrumEnd, &ValueInfo{name: "SpectrumEnd", isBig: true})
	dec.Decode(&v.Spectrum, &ValueInfo{name: "Spectrum", isBig: true})
}

type ColorComponent struct {
	ColorId uint8
	TableDC uint8 `bit:"4"`
	TableAC uint8 `bit:"4"`
}

type CompressData []byte

func (v *CompressData) Unmarshal(dec *Decoder, info *ValueInfo) {
	pos := dec.Pos()
	bs := dec.ReadBytes(0)

	n := 0
	for n < len(bs) && !(bs[n] == 0xFF && bs[n+1] == 0xD9) {
		n++
	}
	*v = bs[:n]
	dec.Seek(pos + n*8)
}

type DqtBody struct {
	Length             uint16
	QtBit              uint8
	QtId               uint8
	ThumbnailRGBBitmap []byte
}

func (v *DqtBody) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Length, &ValueInfo{name: "Length", isBig: true})
	newDec := dec.SubDecoder(int(v.Length - 2))
	newDec.Decode(&v.QtBit, &ValueInfo{name: "QtBit", isBig: true, bit: 4})
	newDec.Decode(&v.QtId, &ValueInfo{name: "QtId", isBig: true, bit: 4})
	newDec.Decode(&v.ThumbnailRGBBitmap, &ValueInfo{name: "ThumbnailRGBBitmap", isBig: true})
	dec.Seek(newDec.Pos())
}

type App0Body struct {
	Length             uint16
	Identifier         AppId
	Version            uint16
	Units              uint8
	DensityX           uint16
	DensityY           uint16
	ThumbnailX         uint8
	ThumbnailY         uint8
	ThumbnailRGBBitmap []byte
}

func (v *App0Body) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Length, &ValueInfo{name: "Length", isBig: true})
	dec.Decode(&v.Identifier, &ValueInfo{name: "Identifier", isBig: true})
	dec.Decode(&v.Version, &ValueInfo{name: "Version", isBig: true})
	dec.Decode(&v.Units, &ValueInfo{name: "Units", isBig: true})
	dec.Decode(&v.DensityX, &ValueInfo{name: "DensityX", isBig: true})
	dec.Decode(&v.DensityY, &ValueInfo{name: "DensityY", isBig: true})
	dec.Decode(&v.ThumbnailX, &ValueInfo{name: "ThumbnailX", isBig: true})
	dec.Decode(&v.ThumbnailY, &ValueInfo{name: "ThumbnailY", isBig: true})
	if v.ThumbnailX*v.ThumbnailY != 0 {
		newDec := dec.SubDecoder(int(v.ThumbnailX * v.ThumbnailY * 3))
		newDec.Decode(&v.ThumbnailRGBBitmap, &ValueInfo{name: "ThumbnailRGBBitmap", isBig: true})
		dec.Seek(newDec.Pos())
	}
}

type AppId string

func (v *AppId) Unmarshal(dec *Decoder, info *ValueInfo) {
	bs := dec.ReadBytes(5)
	n := 0
	for n < len(bs) {
		if bs[n] == 0 {
			break
		}
		n++
	}
	*v = AppId(bs[:n])
}
